package com.hxx.common;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hxx.domain.AiMessage;
import com.hxx.domain.MediaZmx;
import com.hxx.mapper.AiMessgeMapper;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.*;
import org.springframework.ai.model.Media;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/*

可以把AiMessageChatMemory注入到MessageChatMemoryAdvisor中。
MessageChatMemoryAdvisor的作用有下面三个
    用户消息发送给大模型之前，获取会话内的最新n条记录和用户的消息拼接在一起，形成历史消息记录。
    拼接完历史消息之后把用户发送的消息保存到数据库。
    大模型回复完消息之后，将回复的消息保存到数据库。
    -----------------------------------------
    // 注入chatMemory
    private final AiMessageChatMemory chatMemory;
   // 传入chatMemory，会话id，查询最近n条历史消息
   var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, input.getSessionId(), 10);

 */
@Service
@AllArgsConstructor
public class AiMessageChatMemory implements ChatMemory{

    @Autowired
    private AiMessgeMapper messageRepository;

    /**
     * 不实现，手动前端发起请求保存用户的消息和大模型回复的消息
     */
    @Override
    public void add(String conversationId,
                    List<org.springframework.ai.chat.messages.Message> messages) {

    }

    /**
     * 查询会话内的消息最新n条历史记录
     *
     * @param conversationId 会话id
     * @param lastN          最近n条
     * @return org.springframework.ai.chat.messages.Message格式的消息
     */
    @Override
    public List<Message> get(String conversationId, int lastN) {
        LambdaQueryWrapper<AiMessage> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AiMessage::getSessionId, conversationId);
        wrapper.orderByDesc(AiMessage::getCreatedTime);
        wrapper.last("limit " + lastN);

        if (CollectionUtil.isNotEmpty(messageRepository.selectList(wrapper))) {
            return messageRepository.selectList(wrapper)
                    .stream()
                    .map(AiMessageChatMemory::toSpringAiMessage)
                    .toList();
        }
        return null;
    }

    /**
     * 清除会话内的消息
     *
     * @param conversationId 会话id
     */
    @Override
    public void clear(String conversationId) {
        QueryWrapper<AiMessage> wrapper = new QueryWrapper<>();
        wrapper.eq("session_id", conversationId);
        messageRepository.delete(wrapper);
    }

    /**
     * 将自定义的AiMessage对象转换为Spring AI框架中的Message对象
     * 此方法支持三种消息类型：Assistant、User和System的转换
     * 对于用户消息，如果包含媒体文件，将一并转换
     *
     * @param aiMessage 自定义的AiMessage对象，包含消息的基本信息和内容
     * @return 返回转换后的Spring AI框架中的Message对象
     * @throws RuntimeException 如果遇到不支持的消息类型，抛出运行时异常
     */
    @SneakyThrows
    public static org.springframework.ai.chat.messages.Message toSpringAiMessage(AiMessage
                                                                                         aiMessage) {
        // 初始化媒体文件列表
        List<Media> mediaList = new ArrayList<>();
        // 如果消息中包含媒体文件，则转换每个媒体文件为Spring AI框架中的Media对象
        if (!CollectionUtil.isEmpty(aiMessage.getMedias())) {
            mediaList =
                    aiMessage.getMedias().stream().map(AiMessageChatMemory::toSpringAiMedia).toList();
        }
        // 根据消息类型，创建对应的Message对象
        if (aiMessage.getType().equals(MessageType.ASSISTANT)) {
            // 如果是助理消息，创建AssistantMessage对象
            return new AssistantMessage(aiMessage.getTextContent());
        }
        if (aiMessage.getType().equals(MessageType.USER)) {
            // 如果是用户消息，创建UserMessage对象，并包含媒体文件
            return new UserMessage(aiMessage.getTextContent(), mediaList);
        }
        if (aiMessage.getType().equals(MessageType.SYSTEM)) {
            // 如果是系统消息，创建SystemMessage对象
            return new SystemMessage(aiMessage.getTextContent());
        }
        // 如果是不支持的消息类型，抛出异常
        throw new RuntimeException("不支持的消息类型");
    }

    /**
     * 将MediaZmx对象转换为Media对象
     * 此方法使用了MediaZmx类型的media参数，将其type和data属性分别转换为MediaType和URL对象，
     * 并用这些对象来创建一个新的Media对象如果media的data属性不是有效的URL格式，
     * 则抛出运行时异常
     *
     * @param media MediaZmx对象，包含type和data属性，用于创建Media对象
     * @return 返回一个新的Media对象，其类型和数据URL根据media参数的type和data属性创建
     * @throws RuntimeException 如果media的data属性不是有效的URL格式，则抛出此异常
     */
    @SneakyThrows
    public static Media toSpringAiMedia(MediaZmx media)  {
        try {
            // 创建一个新的Media对象，使用MediaZmx的type属性创建MediaType对象，data属性创建URL对象
            return new Media(new MediaType(media.getType()), new URL("http://localhost:8080"));
        }catch (MalformedURLException e){
            // 如果media的data属性不是有效的URL格式，抛出运行时异常
            throw new RuntimeException("url格式错误");
        }
    }
}
